/*
 * CAudioDevice.cpp
 *
 *  Created on: 2016年4月13日
 *      Author: terry
 */

#include "CAudioDevice.h"
#include "CLog.h"
#include "MediaType.h"
#include <algorithm>


namespace av
{

void CAudioDevice::init()
{
    SDL_Init(SDL_INIT_AUDIO);
}

void CAudioDevice::quit()
{
    SDL_Quit();
}

static comn::CriticalSection s_cs;



void CAudioDevice::sdlAudioCallback(void* userdata, Uint8* stream, int len)
{
	CAudioDevice* pThis = (CAudioDevice*)userdata;
	pThis->audioCallback(stream, len);
}


CAudioDevice::CAudioDevice():
		m_audioDev(),
		m_audioSpec(),
		m_playing(false),
		m_volume(SDL_MIX_MAXVOLUME),
		m_muted(false)
{
    CLog::debug("CAudioDevice construct.%p\n", this);

    m_buffer.ensure(1024 * 256);

}

CAudioDevice::~CAudioDevice()
{
	close();
}

bool CAudioDevice::open(int channels, int rate, int bits)
{
    CLog::debug("CAudioDevice open.%p. channels:%d, rate:%d, fmt:%d\n", this, channels, rate, bits);

	SDL_AudioSpec want;
	SDL_zero(want);

    want.freq = rate;
    want.format = AUDIO_S16SYS;
    want.channels = channels;
    want.samples = 1024;
    want.callback = sdlAudioCallback;
    want.userdata = this;
    
    if (bits == 8)
    {
        want.format = AUDIO_U8;
    }

	comn::AutoCritSec lock(s_cs);
	m_audioDev = SDL_OpenAudioDevice(NULL, 0, &want, &m_audioSpec, 0);
	if (m_audioDev == 0)
	{
#ifdef WIN32
		CoInitialize(NULL);
#endif //
		m_audioDev = SDL_OpenAudioDevice(NULL, 0, &want, &m_audioSpec, 0);
		if (m_audioDev == 0)
		{
			const char* err = SDL_GetError();
			CLog::error("CAudioDevice(%p) failed to open SDL device. %s\n", this, err);
		}
	}

	//SDL_PauseAudioDevice(m_audioDev, TRUE);

	return isOpen();
}

void CAudioDevice::close()
{
	if (!isOpen())
	{
		return;
	}

	pause(true);

	{
		comn::AutoCritSec lock2(s_cs);
		comn::AutoCritSec lock(m_cs);
		SDL_CloseAudioDevice(m_audioDev);
		m_audioDev = 0;
	}

	clearFrame();

}

bool CAudioDevice::isOpen()
{
	return (m_audioDev != 0);
}

void CAudioDevice::write(uint8_t* data, int size, int64_t pts)
{
	if (!data || size <= 0)
	{
		return;
	}

	if (!isOpen())
	{
		return;
	}

	m_buffer.write(data, size);
}

void CAudioDevice::pause(bool toPause)
{
	if (m_audioDev == 0)
	{
		return ;
	}

	SDL_PauseAudioDevice(m_audioDev, toPause);

    {
	    comn::AutoCritSec lock(m_cs);
	    m_playing = !toPause;
    }

}

void CAudioDevice::flush()
{
	clearFrame();

}

bool CAudioDevice::isPaused()
{
	comn::AutoCritSec lock(m_cs);

	return !m_playing;
}

float CAudioDevice::getVolume()
{
	float volume = m_volume;
	volume = volume / SDL_MIX_MAXVOLUME;
	return volume;
}

bool CAudioDevice::setVolume(float vol)
{
	int volume = (int)(vol * SDL_MIX_MAXVOLUME);
	if (volume < 0)
	{
		volume = 0;
	}
	else if (volume > SDL_MIX_MAXVOLUME)
	{
		volume = SDL_MIX_MAXVOLUME;
	}

	comn::AutoCritSec lock(m_cs);
	m_volume = volume;

	return true;
}

bool CAudioDevice::isMute()
{
	comn::AutoCritSec lock(m_cs);
	return m_muted;
}

bool CAudioDevice::setMute(bool muted)
{
	comn::AutoCritSec lock(m_cs);

	m_muted = muted;

	return true;
}


void CAudioDevice::audioCallback(Uint8* stream, int len)
{
	memset(stream, m_audioSpec.silence, len);

	int vol = m_volume;
    if (m_muted)
    {
        vol = 0;
    }

    if (m_buffer.readable() < len)
    {
        return;
    }

    uint8_t* data = m_buffer.getReadPtr();

    SDL_MixAudioFormat(stream, data, m_audioSpec.format, len, vol);

    m_buffer.skip(len);
}

void CAudioDevice::clearFrame()
{
    comn::AutoCritSec lock(m_cs);

    m_buffer.clear();
}




} /* namespace av */
